﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GraphBase.Interfaces;

namespace GraphBase
{
    /// <summary>
    /// Does not implement count efficiently.
    /// </summary>
    public class EdgeCollection : IEdgeCollection
    {
        #region Datatypes

        public delegate IEnumerator<Vertex> VertexEnumerator();
        class EdgeEnumerator : IEnumerator
        {
            IEnumerator<Vertex> m_vertexEnum;
            IEnumerator m_edgeEnum;

            public EdgeEnumerator(IEnumerator<Vertex> vertEnum)
            {
                m_vertexEnum = vertEnum;
            }


            #region IEnumerator Members

            public object Current
            {
                get
                {
                    if (m_edgeEnum != null)
                        return m_edgeEnum.Current;
                    return null;
                }
            }

            public bool MoveNext()
            {
                do
                {
                    if (TryGetNextEdge())
                        return true;
                } while (m_vertexEnum.Current != null);
                return false;
            }

            private bool TryGetNextEdge()
            {
                if (m_edgeEnum != null
                    && m_edgeEnum.MoveNext())
                    return true;
                m_edgeEnum = null;
                if (m_vertexEnum.MoveNext())
                    m_edgeEnum = m_vertexEnum.Current.IncomingEdges.GetEnumerator();
                return false;
            }

            public void Reset()
            {
                m_edgeEnum = null;
                m_vertexEnum.Reset();
            }

            #endregion
        }

        #endregion

        protected VertexEnumerator m_enumerator;
        protected OnEdgeAdded m_edgeAdded;
        protected OnEdgeRemoved m_edgeRemoved;

        public EdgeCollection(VertexEnumerator vertexEnum, OnEdgeAdded edgeAdded
            , OnEdgeRemoved edgeRemoved)
        {
            m_enumerator = vertexEnum;
            m_edgeAdded = edgeAdded;
            m_edgeRemoved = edgeRemoved;
        }

        public virtual void Add(IEdge edge)
        {
            Edge realEdge = (Edge)edge;
            Vertex from = (Vertex)edge.IncomingVertex;
            Vertex to = (Vertex)edge.OutgoingVertex;
            zzAdd(realEdge, from, to);
            if (m_edgeAdded != null)
                m_edgeAdded(realEdge, from, to);
        }

        public virtual void Remove(IEdge edge)
        {
            Vertex from = (Vertex)edge.IncomingVertex;
            Vertex to = (Vertex)edge.OutgoingVertex;
            zzRemove((Edge)edge, from, to);
            if (m_edgeRemoved != null)
                m_edgeRemoved((Edge)edge, from, to);
        }

        public virtual void zzAdd(Edge edge, Vertex from, Vertex to)
        {
            from.zzOutgoingEdges.Add(edge);
            to.zzIncomingEdges.Add(edge);
        }

        public virtual void zzRemove(Edge edge, Vertex fromVert, Vertex toVert)
        {
            fromVert.zzOutgoingEdges.Remove(edge);
            toVert.zzIncomingEdges.Remove(edge);
        }

        #region ICollection Members

        public void CopyTo(Array array, int index)
        {
            List<IEdge> edges = new List<IEdge>();
            foreach (IEdge edge in this)
                edges.Add(edge);
            ((ICollection)edges).CopyTo(array, index);
        }

        /// <summary>
        /// Linear time count.  This is a slow method.
        /// </summary>
        public int Count
        {
            get
            {
                int count = 0;
                foreach (IEdge edge in this)
                    ++count;
                return count;
            }
        }

        public bool IsSynchronized
        {
            get { return false; }
        }

        public object SyncRoot
        {
            get { return null; }
        }

        #endregion

        #region IEnumerable Members

        public IEnumerator GetEnumerator()
        {
            return new EdgeEnumerator(m_enumerator());
        }

        #endregion
    }
}
